﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Admx_Generator
{
    internal partial class FrmAddCategory : Form
    {
        private System.Resources.ResourceManager resMan = new System.Resources.ResourceManager("Admx_Generator.Localization.LocalizedStrings", typeof(FrmAddCategory).Assembly);
        private const string _windowsAdmx = @"%WinDir%\PolicyDefinitions\Windows.admx";
        private List<Category> _categories = new List<Category>();
        private Category _parentCategory = null;
        private AdmlFile _currentCulture;
        private AdmlFile _fallbackCulture;

        internal FrmAddCategory(AdmlFile currentLanguage, AdmlFile fallbackLanguage, bool allowImportedCategory)
        {
            InitializeComponent();
            this._currentCulture = currentLanguage;
            this._fallbackCulture = fallbackLanguage;
            this.lblDisplayNameForThisCulture.Text = this.GetLocalizedString("DisplayNameForThisCulture") + _fallbackCulture.ToString() + "] : ";
            
            for (int i = 65; i < 91; i++)
            {
                this.fltTxtBxCategory.AllowedCharacters.Add((char)i);           // lower letters
                this.fltTxtBxCategory.AllowedCharacters.Add((char)(i + 32));    // upper letters
            }
            for (int i = 48; i < 58; i++)
            {
                this.fltTxtBxCategory.AllowedCharacters.Add((char)i);           // numbers
            }
            this.fltTxtBxCategory.AllowedCharacters.Add((char)95);              // underscore
            this.fltTxtBxCategory.Select();
            this.rdBtnAddCategoryFromWindows.Enabled = allowImportedCategory;
            this.rdBtnAddCategoryFromAdmxFile.Enabled = allowImportedCategory;
        }

        #region (internal properties)

        /// <summary>
        /// Get the list of Categories to add.
        /// </summary>
        internal List<Category> Categories { get { return this._categories; } }

        /// <summary>
        /// The parent Category of the Category that the user have manually entered.
        /// </summary>
        internal Category ParentCategory { get { return this._parentCategory; } private set { this._parentCategory = value; } }

        #endregion (internal properties)

        #region (methods)

        internal void PopulateWithWindowsNamespace()
        {
            System.Threading.Tasks.Task populateWithWindowsNamespace = new System.Threading.Tasks.Task(() =>
            {
                Category[] extractedCategories = this.GetCategoriesFromFile(_windowsAdmx);
                Action fillListBox = () => { this.lstBxWindowsNamespaces.Items.AddRange(extractedCategories); };
                this.Invoke(fillListBox);
            });
            populateWithWindowsNamespace.Start();
        }
        
        private Category[] GetCategoriesFromFile(string admxFile)
        {
            List<Category> extractedCategories = new List<Category>();
            try
            {
                System.IO.FileInfo fileInfo = new System.IO.FileInfo(Environment.ExpandEnvironmentVariables(admxFile));

                if (fileInfo.Exists)
                {
                    Dictionary<string, string> stringTable = this.GetStringTable(fileInfo.FullName);

                    XDocument xDoc = XDocument.Load(fileInfo.FullName);
                    XNamespace ns = xDoc.Root.GetDefaultNamespace();
                    XElement target = xDoc.Descendants(ns + "target").ToList<XElement>()[0];
                    List<XElement> categories = xDoc.Descendants(ns + "category").ToList<XElement>();

                    foreach (XElement category in categories)
                    {
                        try
                        {
                            Reference parentCategory = null;

                            try
                            {
                                XElement parentCategoryElement = category.Element(ns + "parentCategory");
                                string refCategory = parentCategoryElement.Attribute("ref").Value;
                                parentCategory = new Reference(refCategory);
                            }
                            catch (Exception) { }
                            string displayName = category.Attribute("displayName").Value;
                            string explainText = category.Attribute("explainText").Value;
                            Category windowsCategory = new Category("windows", displayName, explainText, parentCategory);
                            extractedCategories.Add(windowsCategory);
                        }
                        catch (Exception) { }
                    }
                }
            }
            catch (Exception) { }

            return extractedCategories.ToArray();
        }

        private Dictionary<string, string> GetStringTable(string admxFilePath)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();

            try
            {
                string admxFolder = admxFilePath.Substring(0, admxFilePath.LastIndexOf('\\'));
                string admxFilename = admxFilePath.Substring(admxFilePath.LastIndexOf('\\') + 1);
                string admlFilename = this.GetAdmlFile(admxFolder, admxFilename, this._currentCulture.Culture.Name);
                if (String.IsNullOrEmpty(admlFilename))
                    admlFilename = this.GetAdmlFile(admxFolder, admxFilename, this._fallbackCulture.Culture.Name);
                if (String.IsNullOrEmpty(admlFilename))
                    admlFilename = this.GetAdmlFile(admxFolder, admxFilename, System.Threading.Thread.CurrentThread.CurrentUICulture.Name);
                XDocument xDoc = XDocument.Load(admlFilename);
                XNamespace ns = xDoc.Root.GetDefaultNamespace();
                List<XElement> strings = xDoc.Descendants(ns + "string").ToList<XElement>();

                foreach (XElement element in strings)
                {
                    result.Add(element.Attribute("id").Value, element.Value);
                }
            }
            catch (Exception) { }

            return result;
        }

        private string GetAdmlFile(string admxFolder, string admxFilename, string cultureName)
        {
            string admxShortName = admxFilename.Substring(0, admxFilename.LastIndexOf('.'));
            string admlFilename = admxShortName + ".adml";

            System.IO.DirectoryInfo admlFolder = new System.IO.DirectoryInfo(System.IO.Path.Combine(admxFolder, cultureName));
            if (admlFolder.Exists)
            {
                System.IO.FileInfo[] admlFiles = admlFolder.GetFiles(admlFilename);
                return admlFiles.Length != 0 ? admlFiles[0].FullName : String.Empty;
            }
            return String.Empty;
        }

        private string GetDisplayNameID(string str)
        {
            if (str.StartsWith("$(string.", StringComparison.CurrentCultureIgnoreCase))
                return str.Substring("$(string.".Length, str.Length - "$(string.".Length - 1);

            return str;
        }

        private void ValidateData()
        {
            this.btnOk.Enabled = (this.rdBtnAddThisCategory.Checked && !string.IsNullOrEmpty(this.fltTxtBxCategory.Text) && !String.IsNullOrEmpty(this.txtBxDisplayName.Text)) ||
                (this.rdBtnAddCategoryFromWindows.Checked && this.lstBxWindowsNamespaces.SelectedItems != null && this.lstBxWindowsNamespaces.SelectedItems.Count != 0) ||
                (this.rdBtnAddCategoryFromAdmxFile.Checked && this.lstBxAdmxNamespaces.SelectedItems != null && this.lstBxAdmxNamespaces.SelectedItems.Count != 0);
        }

        /// <summary>
        /// Returns a localized string depending of the current culture.
        /// </summary>
        /// <param name="unlocalizedString">String to localized.</param>
        /// <returns>The localized string.</returns>
        protected string GetLocalizedString(string unlocalizedString)
        {
            string result = string.Empty;

            try
            {
                result = resMan.GetString(unlocalizedString);
                if (!string.IsNullOrEmpty(result))
                    return result;
            }
            catch (Exception) { }

            return "Missing_Localized_String_For(" + ((unlocalizedString != null) ? unlocalizedString : "null") + ")";
        }


        #endregion (methods)

        #region (UI Managment)

        /// <summary>
        /// Enable and Disable Controls as the user change the source of the Category.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CategorySourceChanged(object sender, EventArgs e)
        {
            RadioButton clickedRdBtn = (RadioButton)sender;

            if (string.Compare(clickedRdBtn.Name, "rdBtnAddThisCategory", StringComparison.InvariantCultureIgnoreCase) == 0)
            {
                this.fltTxtBxCategory.Enabled = true;
                this.lstBxWindowsNamespaces.Enabled = false;
                this.lstBxAdmxNamespaces.Enabled = false;
                this.btnBrowse.Enabled = false;
            }
            else if (string.Compare(clickedRdBtn.Name, "rdBtnAddCategoryFromWindows", StringComparison.InvariantCultureIgnoreCase) == 0)
            {
                this.fltTxtBxCategory.Enabled = false;
                this.lstBxWindowsNamespaces.Enabled = true;
                this.lstBxAdmxNamespaces.Enabled = false;
                this.btnBrowse.Enabled = false;
            }
            else if (string.Compare(clickedRdBtn.Name, "rdBtnAddCategoryFromAdmxFile", StringComparison.InvariantCultureIgnoreCase) == 0)
            {
                this.fltTxtBxCategory.Enabled = false;
                this.lstBxWindowsNamespaces.Enabled = false;
                this.lstBxAdmxNamespaces.Enabled = true;
                this.btnBrowse.Enabled = true;
            }

            this.ValidateData();
        }

        private void fltTxtBxCategory_TextChanged(object sender, EventArgs e)
        {
            this.ValidateData();
        }

        private void txtBxDisplayName_TextChanged(object sender, EventArgs e)
        {
            this.ValidateData();
        }

        private void lstBxWindowsNamespaces_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.ValidateData();
        }

        private void lstBxAdmxNamespaces_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.ValidateData();
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFile = new OpenFileDialog();
            openFile.Filter = "*.admx|*.admx";
            openFile.CheckFileExists = true;
            openFile.CheckPathExists = true;
            openFile.DefaultExt = ".admx";
            openFile.InitialDirectory = Environment.ExpandEnvironmentVariables(@"%systemRoot%\PolicyDefinitions");
            openFile.Multiselect = false;
            if (openFile.ShowDialog() == System.Windows.Forms.DialogResult.OK && openFile.CheckFileExists && openFile.FileName.ToLower().EndsWith(".admx"))
            {
                this.lstBxAdmxNamespaces.Items.Clear();
                Category[] extractedCategories = this.GetCategoriesFromFile(openFile.FileName);
                this.lstBxAdmxNamespaces.Items.AddRange(extractedCategories);
                if (extractedCategories.Length == 1)
                { this.lstBxAdmxNamespaces.SelectedIndex = 0; }
            }
        }
        
        private void btnOk_Click(object sender, EventArgs e)
        {
            this.Categories.Clear();

            //Internal Category
            if (this.rdBtnAddThisCategory.Checked)
            {
                Category thisCategory = new Category(this.fltTxtBxCategory.Text, String.Empty, String.Empty, null);
                //thisCategory.Namespace = this._owner.GetNamespace();
                this.Categories.Add(thisCategory);
            }
            else
            {
                // Linked Category
                if (this.rdBtnAddCategoryFromWindows.Checked)   // From Windows.Admx
                {
                    foreach (Category category in this.lstBxWindowsNamespaces.SelectedItems)
                    {
                        this.Categories.Add(category);
                    }
                }
                else
                    if (this.rdBtnAddCategoryFromAdmxFile.Checked)  // From Other Admx
                    {
                        foreach (Category category in this.lstBxAdmxNamespaces.SelectedItems)
                        {
                            this.Categories.Add(category);
                        }
                    }
            }

            DialogResult = System.Windows.Forms.DialogResult.OK;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Categories.Clear();
            DialogResult = System.Windows.Forms.DialogResult.Cancel;
        }

        #endregion (UI Managment)


    }
}
